#pragma once

#include <vector>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include <mutex>
#include <type_traits>

#include "object.h"
#include "class_name.h"

namespace rft
{
    struct reflect_info;

    struct reflect_helper {
        using reflect_table_t = std::unordered_map<std::string_view, const reflect_info>;

        static reflect_table_t& get_reflect_table() {
            static reflect_table_t reflect_table;
            return reflect_table;
        }

        static std::mutex& get_mutex() {
            static std::mutex mtx;
            return mtx;
        }
    };

    class base_reflect_member {
    public:
        std::uintptr_t address; 
        std::vector<std::size_t> array_msg;
        reflect_helper::reflect_table_t::iterator class_info, parent;
    public:
        virtual std::string get(void* base) const = 0;
        virtual void set(void* base, std::string_view value) const = 0;
    };

    struct reflect_member : public base_reflect_member {
        std::string get(void* base) const override;
        void set(void* base, std::string_view value) const override;
    };

    struct reflect_static_member : public base_reflect_member {
        std::string get(void* base) const override;
        void set(void* base, std::string_view value) const override;
    };

    struct reflect_info {
        using member_table_t = std::unordered_map<std::string_view, base_reflect_member*>;
        using construct_callback_t = const std::function<object*(void*)>;

        const std::size_t size;

        member_table_t members;
        construct_callback_t constructor;

        reflect_info(std::size_t _size, member_table_t _members, construct_callback_t _constructor)
            : size{ _size }, members{ _members }, constructor{ _constructor } {}
    };

    template <class T>
    struct reflect_class_registrar {
        constexpr reflect_class_registrar()
            : info_iter{ rft::reflect_helper::get_reflect_table()
                            .try_emplace(get_static_name<T>(),
                                         rft::reflect_info{
                                             sizeof(T), rft::reflect_info::member_table_t{},
                                             [](void* p) -> rft::object* {
                                                 return reinterpret_cast<rft::object*>(new (p) T{});
                                             }})
                            .first } {
        }

        const rft::reflect_helper::reflect_table_t::iterator info_iter;
    };

}  // namespace rft

#define reflect_class(type)                                                          \
    inline static rft::reflect_class_registrar<type> reflect_register_class_helper